package com.gbase8c.dmt.db.metadata;

import com.gbase8c.dmt.db.meta.Meta;
import com.gbase8c.dmt.db.object.*;
import com.gbase8c.dmt.model.migration.config.Snapshot;
import com.gbase8c.dmt.model.migration.dto.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.dbutils.ResultSetHandler;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class AbstractMetadata implements Metadata {

    protected DataSourceDto dto;

    protected Meta meta;
    protected SynonymObject synonymObject;
    protected SequenceObject sequenceObject;
    protected ConstraintObject constraintObject;
    protected FunctionObject functionObject;
    protected IndexObject indexObject;
    protected UserObject userObject;
    protected RoleObject roleObject;
    protected SchemaObject schemaObject;
    protected TableObject tableObject;
    protected ColumnObject columnObject;
    protected TableSpaceObject tableSpaceObject;
    protected ViewObject viewObject;
    protected TriggerObject triggerObject;

    public AbstractMetadata(DataSourceDto dto) {
        this.dto = dto;
    }

    @Override
    public List<String> keywords() {
        return meta.keywords();
    }

    @Override
    public TransObjDto getTransObjDto() {
        TransObjDto transObjDto = TransObjDto.builder().build();
        List<SchemaDto> schemaDtos = getSchemaDtos();
        UserPrivDto userPrivDto = getUserPrivDto();
        List<String> tableSpaceNames = getTableSpaceNames();
        List<TableSpaceDto> tableSpaceDtos = Lists.newArrayList();
        if(tableSpaceNames != null && !tableSpaceNames.isEmpty()){
            for(String tableSpaceName : tableSpaceNames){
                TableSpaceDto tableSpaceDto = TableSpaceDto.builder().build();
                tableSpaceDto.setName(tableSpaceName);
                tableSpaceDtos.add(tableSpaceDto);
            }
        }
        transObjDto.setSchemaDtos(schemaDtos);
        transObjDto.setUserPrivDto(userPrivDto);
        transObjDto.setTableSpaceDtos(tableSpaceDtos);
        return transObjDto;
    }
    @Override
    public UserPrivDto getUserPrivDto() {
        UserPrivDto userPrivDto = UserPrivDto.builder().build();
        List<String> userNames = getUserNames();
        List<String> roleNames = getRoleNames();

        List<UserDto> userDtos = userNames.stream()
                .map(userName ->UserDto.builder().name(userName).build())
                .collect(Collectors.toList());

        List<RoleDto> roleDtos = roleNames.stream()
                .map(roleName ->RoleDto.builder().name(roleName).build())
                .collect(Collectors.toList());

        userPrivDto.setUserDtos(userDtos);
        userPrivDto.setRoleDtos(roleDtos);
        return  userPrivDto;
    }
    @Override
    public List<SchemaDto> getSchemaDtos() {
        List<SchemaDto> schemaDtos = Lists.newArrayList();
        List<String> schemaNames = getSchemaNames();
        for (String schemaName : schemaNames) {
            List<String> tableNames = getTableNames(schemaName);
            List<IndexDto> indexDtoList = getIndexDtos(schemaName);
            List<String> viewNames = getViewNames(schemaName);
            List<String> functionNames = getFunctionNames(schemaName);
            List<ConstraintDto> constraintDtoList = getConstraintDtos(schemaName);
            List<SequenceDto> sequenceDtoList = getSequencesDtos(schemaName);
//            List<String> synonymNames = getSynonymNames(schemaName);
            List<SynonymDto> synonymDtoList = getSynonymDtos(schemaName);

            List<TableDto> tableDtos = tableNames.stream()
                    .map(tableName ->TableDto.builder().name(tableName).schema(schemaName).build())
                    .collect(Collectors.toList());
            //添加视图列表
            List<ViewDto> viewDtos = viewNames.stream()
                    .map(viewName ->ViewDto.builder().name(viewName).schema(schemaName).build())
                    .collect(Collectors.toList());
            List<IndexDto> indexDtos = indexDtoList.stream()
                    .map(indexDto -> IndexDto.builder().name(indexDto.getName())
                            .schema(schemaName).tableOwner(indexDto.getTableOwner()).tableName(indexDto.getTableName()).build())
                    .collect(Collectors.toList());
            List<FunctionDto> functionDtos = functionNames.stream()
                    .map(functionName -> FunctionDto.builder().name(functionName).schema(schemaName).build())
                    .collect(Collectors.toList());
            List<ConstraintDto> constraintDtos = constraintDtoList.stream()
                    .map(constraintDto -> ConstraintDto.builder().name(constraintDto.getName()).srcType(constraintDto.getSrcType()).schema(schemaName).tableName(constraintDto.getTableName()).build())
                    .collect(Collectors.toList());
            List<SequenceDto> sequenceDtos = sequenceDtoList.stream()
                    .map(sequenceDto -> SequenceDto.builder().name(sequenceDto.getName()).schema(schemaName).build())
                    .collect(Collectors.toList());
            List<SynonymDto> synonymDtos = synonymDtoList.stream()
                    .map(synonymDto ->SynonymDto.builder().name(synonymDto.getName()).schema(synonymDto.getSchema()).build())
                    .collect(Collectors.toList());
            SchemaDto dto = SchemaDto.builder()
                    .name(schemaName)
                    .tableDtos(tableDtos)
                    .schema(schemaName)
                    .indexDtos(indexDtos)
                    .viewDtos(viewDtos)
                    .functionDtos(functionDtos)
                    .constraintDtos(constraintDtos)
                    .sequenceDtos(sequenceDtos)
                    .synonymDtos(synonymDtos)
                    .build();
            schemaDtos.add(dto);
        }
        return schemaDtos;
    }

    @Override
    public List<String> getUserNames() {
        return schemaObject.getNames(null);
    }
    @Override
    public UserDto getUserDto(String userName) {
        return userObject.get(userName, null);
    }
    @Override
    public UserDto userPriConvert(UserDto userDto) {
        return userObject.convert(userDto, null);
    }
    @Override
    public String userPrivSql(UserDto userDto) {
        return userObject.sql(userDto, null);
    }


    @Override
    public List<String> getRoleNames() {
        return roleObject.getNames(null);
    }
    @Override
    public RoleDto getRoleDto(String roleName) {
        return roleObject.get(roleName, null);
    }
    @Override
    public RoleDto rolePriConvert(RoleDto roleDto) {
        return roleObject.convert(roleDto, null);
    }
    @Override
    public String rolePrivSql(RoleDto roleDto) {
        return roleObject.sql(roleDto, null);
    }


    @Override
    public List<String> getSchemaNames() {
        return schemaObject.getNames(null);
    }
    @Override
    public List<String> getSchemaNames(boolean includeSystemSchemas) {
        return schemaObject.getNames(null, includeSystemSchemas);
    }
    @Override
    public SchemaDto getSchemaDto(String schemaName) {
        return schemaObject.get(schemaName, null);
    }
    @Override
    public SchemaDto schemaConvert(SchemaDto schemaDto) {
        return schemaObject.convert(schemaDto, null);
    }
    @Override
    public String schemaSql(SchemaDto schemaDto) {
        return schemaObject.sql(schemaDto, null);
    }


    @Override
    public List<String> getTableSpaceNames() {
        return tableSpaceObject.getNames(null);
    }
    @Override
    public TableSpaceDto getTableSpaceDto(String tableSpaceName) {
        return tableSpaceObject.get(tableSpaceName, null);
    }
    @Override
    public TableSpaceDto tableSpaceConvert(TableSpaceDto tableSpaceDto) {
        return tableSpaceObject.convert(tableSpaceDto, null);
    }
    @Override
    public String tableSpaceSql(TableSpaceDto tableSpaceDto) {
        return tableSpaceObject.sql(tableSpaceDto, null);
    }


    @Override
    public List<String> getTableNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return tableObject.getNames(params);
    }
    @Override
    public TableDto getTableDto(String schema, String tableName, List<String> tableSpaceNames) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        params.put("tableSpaceNames", tableSpaceNames);
        return tableObject.get(tableName, params);
    }
    @Override
    public TableDto tableConvert(TableDto tableDto) {
        Map<String, Object> params = Maps.newHashMap();
        return tableObject.convert(tableDto, params);
    }
    @Override
    public List<String> tableSql(TableDto tableDto) {
        return tableObject.tableSql(tableDto);
    }

    @Override
    public List<TableTypeMapperDto> tableTypesMapper(String schema) {
        return tableObject.tableTypeMapper(schema);
    }

    @Override
    public BigDecimal dataSize(String schema, String tableName) {
        return tableObject.dataSize(schema, tableName);
    }

    @Override
    public List<ColumnType> columnTypes(String schema, String type, List<String> tableNames) {
        return columnObject.columnTypes(schema, type, tableNames);
    }

    @Override
    public List<TableDto.ColumnDto> columnDtos(String schema, String tableName) {
        return columnObject.columnDtos(schema, tableName);
    }

    @Override
    public List<String> getViewNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return viewObject.getNames(params);
    }
    @Override
    public ViewDto getViewDto(String schema, String viewName) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return viewObject.get(viewName, params);
    }
    @Override
    public ViewDto viewConvert(ViewDto viewDto) {
        return viewObject.convert(viewDto, null);
    }
    @Override
    public String viewSql(ViewDto viewDto) {
        return viewObject.sql(viewDto, null);
    }


    @Override
    public List<String> getFunctionNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return functionObject.getNames(params);
    }
    @Override
    public FunctionDto getFunctionDto(String schema, String functionName) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return functionObject.get(functionName, params);
    }
    @Override
    public FunctionDto functionConvert(FunctionDto functionDto, DataSourceDto srcDataSource, DataSourceDto tarDataSource) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("srcDataSource", srcDataSource);
        params.put("tarDataSource", tarDataSource);
        return functionObject.convert(functionDto, params);
    }
    @Override
    public String functionSql(FunctionDto functionDto) {
        return functionObject.sql(functionDto, null);
    }


    @Override
    public List<ConstraintDto> getConstraintDtos(String schema) {
        return constraintObject.getConstraintDtos(schema);
    }
    @Override
    public List<ConstraintDto> getConstraintDtos(String schema, String table) {
        return constraintObject.getConstraintDtos(schema, table);
    }
    @Override
    public ConstraintDto getConstraintDto(String schema, ConstraintDto constraintDto) {
        return constraintObject.getConstraintDto(schema, constraintDto);
    }
    @Override
    public ConstraintDto constraintConvert(ConstraintDto constraintDto) {
        Map<String, Object> params = Maps.newHashMap();
        return constraintObject.convert(constraintDto, params);
    }
    @Override
    public ConstraintDto constraintConvert(ConstraintDto constraintDto, List<String> tableNameList) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("tableNameList",tableNameList);
        return constraintObject.convert(constraintDto, params);
    }
    @Override
    public String constraintSql(ConstraintDto constraintDto) {
        return constraintObject.sql(constraintDto, null);
    }


    @Override
    public List<String> getSequencesNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return sequenceObject.getNames(params);
    }
    @Override
    public SequenceDto getSequenceDto(String schema, String sequenceName) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return sequenceObject.get(sequenceName, params);
    }
    @Override
    public SequenceDto sequenceConvert(SequenceDto sequenceDto) {
        Map<String, Object> params = Maps.newHashMap();
        return sequenceObject.convert(sequenceDto, params);
    }
    @Override
    public String sequenceSql(SequenceDto sequenceDto) {
        return sequenceObject.sql(sequenceDto,null);
    }


    @Override
    public List<String> getSynonymNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return synonymObject.getNames(params);
    }
    @Override
    public List<SynonymDto> getSynonymDtos(String schema) {
        return synonymObject.getSynonymDtos(schema);
    }
    @Override
    public List<SequenceDto> getSequencesDtos(String schema) {
        return sequenceObject.getSequenceDtos(schema);
    }
    @Override
    public SynonymDto getSynonymDto(String schema, String synonymName) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return synonymObject.get(synonymName, params);
    }
    @Override
    public SynonymDto synonymConvert(SynonymDto synonymDto) {
        return synonymObject.convert(synonymDto, null);
    }
    @Override
    public String synonynSql(SynonymDto synonymDto) {
        return synonymObject.sql(synonymDto,null);
    }

    @Override
    public List<String> getTriggerNames(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return triggerObject.getNames(params);
    }

    @Override
    public TriggerDto getTriggerDto(String schema, String triggerName) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        return triggerObject.get(triggerName, params);
    }

    @Override
    public TriggerDto triggerConvert(TriggerDto triggerDto) {
        Map<String, Object> params = Maps.newHashMap();
        return triggerObject.convert(triggerDto, params);
    }

    @Override
    public List<String> triggerSql(TriggerDto triggerDto) {
        return triggerObject.triggerSql(triggerDto);
    }


    @Override
    public List<IndexDto> getIndexDtos(String schema) {
        return indexObject.getIndexDtos(schema);
    }

    @Override
    public List<IndexDto> getIndexDtos(String schema, String table) {
        return indexObject.getIndexDtos(schema, table);
    }

    @Override
    public IndexDto getIndexDto(String schema, IndexDto indexDto, List<String> tableSpaceNames) {
        return indexObject.getIndexDto(schema,indexDto,tableSpaceNames);
    }

    @Override
    public IndexDto indexConvert(IndexDto indexDto) {
        return indexObject.convert(indexDto, null);
    }
    @Override
    public String indexSql(IndexDto indexDto) {
        return indexObject.sql(indexDto, null);
    }

    @Override
    public List<String> getDataTypes() {
        return meta.getDataTypes();
    }

    @Override
    public Integer toSqlType(String dataType) {
        return meta.toSqlType(dataType);
    }

    @Override
    public String toDataType(Integer sqlType) {
        return meta.toDataType(sqlType);
    }

    @Override
    public ColumnType toColumnType(ColumnType srcColumnType) {
        return columnObject.toColumnType(srcColumnType);
    }

    @Override
    public String characterSet(String database) {
        return meta.characterSet(database);
    }

    @Override
    public Snapshot snapshot() {
        return meta.snapshot();
    }

    @Override
    public String wrap(String name, boolean preserveCase) {
        return meta.wrap(name, preserveCase);
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) {
        return meta.query(sql, rsh, params);
    }

    @Override
    public int execute(String sql, Object... params) {
        return meta.execute(sql, params);
    }

    @Override
    public boolean valid(Snapshot snapshot) {
        return meta.valid(snapshot);
    }
}
